
#include "Mesh.h"
#include "Geometry.h"
#include "Engine.h"
#include "BBox.h"
#include "Entity.h"
#include <cmath>

//
// Constructor
//

Mesh::Mesh()
{
	updateVB = true;
	updateTB = true;
	updateQB = true;

	vertexBuffer = 0;
	triangleBuffer = 0;
	quadBuffer = 0;
}

//
// AddVertex
//

int Mesh::AddVertex(Vertex &v)
{
	updateVB = Geometry::updateNormals = true;
	vertices.push_back(v);
		return vertices.size()-1;
}

int Mesh::AddVertex(Real3 position, Real3 normal, Real2 tex)
{
	return AddVertex(Vertex(position, normal, tex));
}

//
// AddUniqueVertex
//

int Mesh::AddUniqueVertex(Real3 position, Real3 normal, Real2 tex)
{
	for(uint i = 0; i < vertices.size(); ++i)
		if(vertices[i].position == position)
			return i;

	return AddVertex(Vertex(position, normal, tex));
}

//
// AddTriangle
//

int Mesh::AddTriangle(Triangle &t)
{
	updateTB = true;
	triangles.push_back(t);
	return triangles.size()-1;
}

int Mesh::AddTriangle(uint a, uint b, uint c)
{
	return AddTriangle(Triangle(a, b, c));
}

//
// AddQuad
//

int Mesh::AddQuad(Quad &q)
{
	updateQB = true;
	quads.push_back(q);
	return quads.size()-1;
}

int Mesh::AddQuad(uint a, uint b, uint c, uint d)
{
	return AddQuad(Quad(a, b, c, d));
}

//
// Calculate Vertex Normals
//

void Mesh::CalculateVertexNormals()
{
	#ifdef USE_NORMALIZE_IN_CALCULATIONS
	for(uint i = 0; i < vertices.size(); ++i)
		vertices[i].normal.Set(0.0f, 0.0f, 0.0f);
	#else
	vector< vector<Real3> > normals(vertices.size());
	#endif

	for(uint i = 0; i < triangles.size(); ++i)
	{
		Real3 normal = triangles[i].Normal(&vertices);
		
		#ifdef USE_NORMALIZE_IN_CALCULATIONS
		vertices[triangles[i].a].normal += normal;
		vertices[triangles[i].b].normal += normal;
		vertices[triangles[i].c].normal += normal;
		#else
		normals[triangles[i].a].push_back(normal);
		normals[triangles[i].b].push_back(normal);
		normals[triangles[i].c].push_back(normal);
		#endif
	}

	for(uint i = 0; i < quads.size(); ++i)
	{
		Real3 normal = quads[i].Normal(&vertices);

		#ifdef USE_NORMALIZE_IN_CALCULATIONS
		vertices[quads[i].a].normal += normal;
		vertices[quads[i].b].normal += normal;
		vertices[quads[i].c].normal += normal;
		vertices[quads[i].d].normal += normal;
		#else
		normals[quads[i].a].push_back(normal);
		normals[quads[i].b].push_back(normal);
		normals[quads[i].c].push_back(normal)
		normals[quads[i].d].push_back(normal);
		#endif
	}

	for(uint i = 0; i < vertices.size(); ++i)
	{
		#ifdef USE_NORMALIZE_IN_CALCULATIONS
		vertices[i].normal.Normalize();
		#else
		Real3 total;
		for(uint j = 0; j < normals[i].size(); ++j)
			total += normals[i][j];
		total *= (1.0f / normals[i].size());

		vertices[i].normal = total;
		#endif
	}
}

//
// CalculateTextureCoordinates
//

void Mesh::CalculateTextureCoordinates(BBox *box, float scale, TexCoordType type)
{
#ifdef USE_TEX
	// Update Buffer
	updateVB = true;

	float iX = 1.0f / box->X();
	float iZ = 1.0f / box->Z();

	if(type == RECTANGULAR)
	{
		for(uint i = 0; i < vertices.size(); i++)
		{
			vertices[i].tex.u = (vertices[i].position.x - box->Min().x) * iX * scale;
			vertices[i].tex.v = (vertices[i].position.z - box->Min().z) * iZ * scale;
		}
	}
	else if(type == CYLINDRICAL)
	{
		scale /= 360.0f;
		for(uint i = 0; i < vertices.size(); i++)
		{
			vertices[i].tex.v = (vertices[i].position.z - box->Min().z) * iZ * scale;
			vertices[i].tex.u = atan((vertices[i].position.z - box->Min().z) / 
				                       (vertices[i].position.x - box->Min().x)) * scale;
		}
	}
	else
	{
		/*for(uint i = 0; i < vertices.size(); i++)
		{
			//float d = sqrt(
			vertices[i].tex.u = atan((vertices[i].position.z - box->Min().z) / 
				                       (vertices[i].position.z - box->Min().z)) * scale / 360;
			vertices[i].tex.u = atan((vertices[i].position.z - box->Min().z) / 
				                       (vertices[i].position.x - box->Min().x)) * scale / 360;
		}*/
	}
#endif
}

//
// Helper Class
//

class VTQ_List
{
public:
	VTQ_List() { prev = -1; }
	vector<ushort> tri;
	vector<ushort> quad;
	int prev;
};

//
// MergeDuplicateVertices
//

void Mesh::MergeDuplicateVertices()
{
	vector<VTQ_List> vert(vertices.size());

	// Populate vert array with triangles
	for(uint i = 0; i < triangles.size(); ++i)
	{
		vert[triangles[i].a].tri.push_back(i);
		vert[triangles[i].b].tri.push_back(i);
		vert[triangles[i].c].tri.push_back(i);
	}

	// Populate vert array with quads
	for(uint i = 0; i < quads.size(); ++i)
	{
		vert[quads[i].a].quad.push_back(i);
		vert[quads[i].b].quad.push_back(i);
		vert[quads[i].c].quad.push_back(i);
		vert[quads[i].d].quad.push_back(i);
	}

	// Check for duplicate vertices & set prev to the
	// first instance of the duplicates
	for(uint i = 0; i < vertices.size()-1; ++i)
	{
		if(vert[i].prev == -1)
		{
			for(ushort j = i+1; j < vertices.size(); ++j)
			{
				if(vertices[i].position == vertices[j].position)
					vert[j].prev = i;
			}
		}
	}

	// Set all triangles and quads that reference
	// duplicated vertices to the first instance
	for(uint i = 0; i < vertices.size(); ++i)
	{
		if(vert[i].prev != -1)
		{
			for(ushort j = 0; j < vert[i].tri.size(); ++j)
			{
				if(triangles[vert[i].tri[j]].a == i)      triangles[vert[i].tri[j]].a = vert[i].prev;
				else if(triangles[vert[i].tri[j]].b == i) triangles[vert[i].tri[j]].b = vert[i].prev;
				else                                      triangles[vert[i].tri[j]].c = vert[i].prev;
			}
			
			for(ushort j = 0; j < vert[i].quad.size(); ++j)
			{
				if(quads[vert[i].quad[j]].a == i)      quads[vert[i].quad[j]].a = vert[i].prev;
				else if(quads[vert[i].quad[j]].b == i) quads[vert[i].quad[j]].b = vert[i].prev;
				else if(quads[vert[i].quad[j]].c == i) quads[vert[i].quad[j]].c = vert[i].prev;
				else                                   quads[vert[i].quad[j]].d = vert[i].prev;
			}
		}
	}

	// Delete old, unused vertices & decrease 
	// the indices for vertices greater that deleted
	for(uint i = vert.size()-1; i > 0; --i)
	{
		if(vert[i].prev != -1)
			SafeRemoveVertex(i);
	}

	for(uint i = 0; i < quads.size(); ++i)
	{
		quads[i].Update2s();
	}

	updateVB = true;
	updateTB = true;
	updateQB = true;
}

//
// SafeRemoveVertex
//

void Mesh::SafeRemoveVertex(uint i)
{
	vertices.erase(vertices.begin() + i);
	DecreaseIndices(i);
}

//
// DecreaseIndices
//

void Mesh::DecreaseIndices(uint max)
{
	for(uint i = 0; i < triangles.size(); ++i)
	{
		if(triangles[i].a > max) triangles[i].a--;
		if(triangles[i].b > max) triangles[i].b--;
		if(triangles[i].c > max) triangles[i].c--;
	}

	for(uint i = 0; i < quads.size(); ++i)
	{
		if(quads[i].a > max) quads[i].a--;
		if(quads[i].b > max) quads[i].b--;
		if(quads[i].c > max) quads[i].c--;
		if(quads[i].d > max) quads[i].d--;
	}
}

//
// Clear
//

void Mesh::Clear()
{
	vertices.clear();
	triangles.clear();
	quads.clear();
	
	updateVB = true;
	updateTB = true;
	updateQB = true;
}

//
// CreateBuffers
//

void Mesh::CreateBuffers()
{
	CreateVBuffer();
	CreateTBuffer();
	CreateQBuffer();
}

//
// LoadBuffers
//

void Mesh::LoadBuffers(uchar renderFlag)
{
	if(!vertices.empty())
	{
		LoadVBuffer();
		LoadTBuffer();
		if(triangles.empty() == false) 
			Render(renderFlag, true);
		LoadQBuffer();
		if(quads.empty() == false) 
			Render(renderFlag, false);

		engine.AddVertices(vertices.size());
		engine.AddFaces(triangles.size() + quads.size());
	}
}

//
// Render
//

void Mesh::Render(uchar renderFlag, bool renderTris) 
{
	uint vSize = vertices.size();

	if(renderFlag == 0)
		engine.direct3DRenderer->SetRenderState(D3DRS_FILLMODE, D3DFILL_WIREFRAME);
	else
		engine.direct3DRenderer->SetRenderState(D3DRS_FILLMODE, D3DFILL_SOLID);

	if(renderFlag == 1)
		engine.direct3DRenderer->DrawPrimitive(D3DPT_POINTLIST, 0, vSize);
	else
	{
		if(renderTris)
			engine.direct3DRenderer->DrawIndexedPrimitive(
				D3DPT_TRIANGLELIST, 0, 0, vSize, 0, triangles.size());
		else
			engine.direct3DRenderer->DrawIndexedPrimitive(
				D3DPT_TRIANGLELIST, 0, 0, vSize, 0, quads.size()*2);
	}
}

//
// CreateVBuffer
//

void Mesh::CreateVBuffer() 
{
	if(!vertices.empty() && updateVB)
	{
		updateVB = false;

		// Clear Old buffer
		if(vertexBuffer)
			vertexBuffer->Release();

		// Size
		int size = sizeof(Vertex) * vertices.size();

		// Create new buffer
		engine.direct3DRenderer->CreateVertexBuffer(
				size, 0, D3DFVF_CUSTOMVERTEX,
				D3DPOOL_DEFAULT, &vertexBuffer, NULL);

		// Copy over data
		void *data;
		vertexBuffer->Lock(0, size, (void**)&data, 0);
		memcpy(data, &vertices[0], size);
		vertexBuffer->Unlock();
	}
}

//
// CreateTBuffer
//

void Mesh::CreateTBuffer() 
{
	if(!triangles.empty() && updateTB)
	{
		updateTB = false;

		// Clear old buffer
		if(triangleBuffer)
			triangleBuffer->Release();

		// Size
		int size = sizeof(Triangle) * triangles.size();

		// Create new buffer
		#ifdef BUFFER_BIT_16
		engine.direct3DRenderer->CreateIndexBuffer(
				size,	0, D3DFMT_INDEX16,
				D3DPOOL_DEFAULT, &triangleBuffer, NULL);
		#else
		engine.direct3DRenderer->CreateIndexBuffer(
				size,	0, D3DFMT_INDEX32,
				D3DPOOL_DEFAULT, &triangleBuffer, NULL);
		#endif

		// Copy over data
		void *data;
		triangleBuffer->Lock(0, size, (void**)&data, 0);
		memcpy(data, &triangles[0], size);
		triangleBuffer->Unlock();
	}
}

//
// CreateQBuffer
//

void Mesh::CreateQBuffer() 
{
	if(!quads.empty() && updateQB)
	{
		updateQB = false;

		// Clear old buffer
		if(quadBuffer)
			quadBuffer->Release();

		// Size
		int size = sizeof(Quad) * quads.size();

		// Create new buffer
		#ifdef BUFFER_BIT_16
		engine.direct3DRenderer->CreateIndexBuffer(
				size,	0, D3DFMT_INDEX16,
				D3DPOOL_DEFAULT, &quadBuffer, NULL);
		#else
		engine.direct3DRenderer->CreateIndexBuffer(
				size,	0, D3DFMT_INDEX32,
				D3DPOOL_DEFAULT, &quadBuffer, NULL);
		#endif

		// Copy over data
		void *data;
		quadBuffer->Lock(0, size, (void**)&data, 0);
		memcpy(data, &quads[0], size);
		quadBuffer->Unlock();
	}
}

//
// LoadVBuffer
//

void Mesh::LoadVBuffer() 
{
  engine.direct3DRenderer->SetStreamSource(0, vertexBuffer, 0, sizeof(Vertex));
  engine.direct3DRenderer->SetFVF(D3DFVF_CUSTOMVERTEX);
}

//
// LoadTBuffer
//

void Mesh::LoadTBuffer() 
{
	if(!triangles.empty())
		engine.direct3DRenderer->SetIndices(triangleBuffer);
}

//
// LoadQBuffer
//

void Mesh::LoadQBuffer() 
{
	if(!quads.empty())
		engine.direct3DRenderer->SetIndices(quadBuffer);
}

//
// CleanUp
//

void Mesh::CleanUp() 
{
	if(vertexBuffer)   vertexBuffer->Release();
	if(triangleBuffer) triangleBuffer->Release();
	if(quadBuffer)     quadBuffer->Release();
}

//
// CalculateBBox
//

void Mesh::CalculateBBox(BBox *box)
{
	for(uint i = 0; i < vertices.size(); ++i)
		box->AddPoint(vertices[i].position);
}

void Mesh::CalculateBBoxOctree(Entity *e)
{
	for(uint i = 0; i < triangles.size(); ++i)
		e->AddOctreeTri(vertices[triangles[i].a].position,
										vertices[triangles[i].b].position,
										vertices[triangles[i].c].position);

	for(uint i = 0; i < quads.size(); ++i)
	{
		e->AddOctreeTri(vertices[quads[i].a].position,
										vertices[quads[i].b].position,
										vertices[quads[i].c].position);
		e->AddOctreeTri(vertices[quads[i].a].position,
										vertices[quads[i].c].position,
										vertices[quads[i].d].position);
	}
}

//
// Rotate Verts
//

void Mesh::RotateVertices(const D3DXMATRIX *rotation)
{
	if(vertices.size())
	{
		D3DXVec3TransformCoordArray((D3DXVECTOR3*)&vertices[0].position, sizeof(Vertex), 
																(D3DXVECTOR3*)&vertices[0].position, sizeof(Vertex), 
																rotation, vertices.size());

		D3DXVec3TransformCoordArray((D3DXVECTOR3*)&vertices[0].normal, sizeof(Vertex), 
																(D3DXVECTOR3*)&vertices[0].normal, sizeof(Vertex), 
																rotation, vertices.size());
	}
}

//
// Write
//

void Mesh::Write(FileStream &file)
{
	if(file.IsBinary())
	{
		uint vsize = vertices.size();
		file.Write(((char*)&vsize), sizeof(uint));

		for(uint i = 0; i < vertices.size(); ++i)
			vertices[i].position.Write(file);
		for(uint i = 0; i < vertices.size(); ++i)
			vertices[i].normal.Write(file); 
		#ifdef USE_COLOR
		file.WriteFlag(true);
		for(uint i = 0; i < vertices.size(); ++i)
			vertices[i].color.Write(file); 
		#else
		file.WriteFlag(false);
		#endif
		#ifdef USE_TEX
		file.WriteFlag(true);
		for(uint i = 0; i < vertices.size(); ++i)
			vertices[i].tex.Write(file); 
		#else
		file.WriteFlag(false);
		#endif
		
		uint tsize = triangles.size();
		file.Write(((char*)&tsize), sizeof(uint));

		for(uint i = 0; i < tsize; ++i)
		#ifdef BUFFER_BIT_16
		{
			int a[3] = { triangles[i].a, triangles[i].b, triangles[i].c };
			file.Write((char*)a, sizeof(a));
		}
		#else
			file.Write((char*)&triangles[i], sizeof(Triangle));
		#endif
		
		uint qsize = quads.size();
		file.Write(((char*)&qsize), sizeof(uint));

		for(uint i = 0; i < qsize; ++i)
		#ifdef BUFFER_BIT_16
		{
			int a[6] = { quads[i].a, quads[i].b, quads[i].c, quads[i].a2, quads[i].c2, quads[i].d };
			file.Write((char*)a, sizeof(a));
		}
		#else
			file.Write((char*)&quads[i], sizeof(Quad));
		#endif
	}
	else
	{
		file.oFile << "Mesh\n";
		file.oFile << "{\n";

		file.oFile << "Vertex " << (int)vertices.size() << "\n";
		file.oFile << "{\n";
		for(uint i = 0; i < vertices.size(); ++i)
		{ file.oFile << "Position "; vertices[i].position.Write(file); }
		for(uint i = 0; i < vertices.size(); ++i)
		{ file.oFile << "Normal ";   vertices[i].normal.Write(file); }
		#ifdef USE_COLOR
		for(uint i = 0; i < vertices.size(); ++i)
		{ file.oFile << "Color ";    vertices[i].color.Write(file); }
		#endif
		#ifdef USE_TEX
		for(uint i = 0; i < vertices.size(); ++i)
		{ file.oFile << "TexCoord "; vertices[i].tex.Write(file); }
		#endif
		file.oFile << "}\n";

		if(!triangles.empty())
		{
			file.oFile << "Triangle " << (int)triangles.size() << "\n";
			file.oFile << "{\n";
			for(uint i = 0; i < triangles.size(); ++i)
				file.oFile << "(" << triangles[i].a << ", " << triangles[i].b << ", " << triangles[i].c << ")\n"; 
			file.oFile << "}\n";
		}

		if(!quads.empty())
		{
			file.oFile << "Quad " << (int)quads.size() << "\n";
			file.oFile << "{\n";
			for(uint i = 0; i < quads.size(); ++i)
				file.oFile << "(" << quads[i].a << ", " << quads[i].b << ", " << quads[i].c << ", " << quads[i].d << ")\n"; 
			file.oFile << "}\n";
		}

		file.oFile << "}\n";
	}
}

//
// Read
//

void Mesh::Read(FileStream &file)
{
	if(file.IsBinary())
	{
		uint v; 
		file.Read((char*)&v, sizeof(uint));
		vertices.resize(v);

		for(uint i = 0; i < vertices.size(); ++i)
			vertices[i].position.Read(file);

		for(uint i = 0; i < vertices.size(); ++i)
			vertices[i].normal.Read(file); 

		if(file.ReadFlag())
			for(uint i = 0; i < vertices.size(); ++i)
		#ifdef USE_COLOR
				vertices[i].color.Read(file); 
		#else
			{ Color temp; temp.Read(file); }
		#endif

		if(file.ReadFlag())
			for(uint i = 0; i < vertices.size(); ++i)
		#ifdef USE_TEX
				vertices[i].tex.Read(file); 
		#else
			{ Real2 temp; temp.Read(file); }
		#endif

		uint t; 
		file.Read((char*)&t, sizeof(uint));
		triangles.resize(t);

		for(uint i = 0; i < triangles.size(); ++i)
		#ifdef BUFFER_BIT_16
		{
			uint a[3];
			file.Read((char*)a, 3*sizeof(uint));
			triangles[i].a = a[0];
			triangles[i].b = a[1];
			triangles[i].c = a[2];
		}
		#else
			file.Read((char*)&triangles[i], sizeof(Triangle));
		#endif

		uint q; 
		file.Read((char*)&q, sizeof(uint));
		quads.resize(q);

		for(uint i = 0; i < quads.size(); ++i)
		#ifdef BUFFER_BIT_16
		{
			uint a[6];
			file.Read((char*)a, 6*sizeof(uint));
			quads[i].a = a[0];
			quads[i].b = a[1];
			quads[i].c = a[2];
			quads[i].a2 = a[3];
			quads[i].c2 = a[4];
			quads[i].d = a[5];
		}
		#else
			file.Read((char*)&quads[i], sizeof(Quad));
		#endif
	}
	else
	{
		/*file << "Mesh\n";
		file << "{\n";

		file << "Vertex " << (int)vertices.size() << "\n";
		file << "{\n";
		for(uint i = 0; i < vertices.size(); ++i)
		{ file << "Position "; vertices[i].position.Write(file, false); }
		for(uint i = 0; i < vertices.size(); ++i)
		{ file << "Normal ";   vertices[i].normal.Write(file, false); }
		#ifdef USE_COLOR
		for(uint i = 0; i < vertices.size(); ++i)
		{ file << "Color ";    vertices[i].color.Write(file, false); }
		#endif
		#ifdef USE_TEX
		for(uint i = 0; i < vertices.size(); ++i)
		{ file << "TexCoord "; vertices[i].tex.Write(file, false); }
		#endif
		file << "}\n";

		if(!triangles.empty())
		{
			file << "Triangle " << (int)triangles.size() << "\n";
			file << "{\n";
			for(uint i = 0; i < triangles.size(); ++i)
				file << "(" << triangles[i].a << ", " << triangles[i].b << ", " << triangles[i].c << ")\n"; 
			file << "}\n";
		}

		if(!quads.empty())
		{
			file << "Quad " << (int)quads.size() << "\n";
			file << "{\n";
			for(uint i = 0; i < quads.size(); ++i)
				file << "(" << quads[i].a << ", " << quads[i].b << ", " << quads[i].c << ", " << quads[i].d << ")\n"; 
			file << "}\n";
		}

		file << "}\n";*/
	}

	updateVB = true;
	updateTB = true;
	updateQB = true;
}
